package pers.cfeng.server.dataManage;

import pers.cfeng.common.AbstractDataCache;
import pers.cfeng.common.DatabaseException;
import pers.cfeng.common.ErrorCode;
import pers.cfeng.server.dataManage.dataItem.DataItem;
import pers.cfeng.server.dataManage.dataItem.DataItemImpl;
import pers.cfeng.server.dataManage.logger.Logger;
import pers.cfeng.server.dataManage.page.Page;
import pers.cfeng.server.dataManage.page.PageCommon;
import pers.cfeng.server.dataManage.page.PageFirst;
import pers.cfeng.server.dataManage.pageCache.PageCache;
import pers.cfeng.server.dataManage.pageIndex.PageIndex;
import pers.cfeng.server.dataManage.pageIndex.PageInfo;
import pers.cfeng.server.dataManage.recover.DataRecover;
import pers.cfeng.server.txManage.TransactionManager;
import pers.cfeng.server.utils.FaultHandler;
import pers.cfeng.server.utils.Types;

/**
 * 数据管理
 */

public class DataManagerImpl extends AbstractDataCache<DataItem> implements DataManager {
    PageCache pageCache;
    private TransactionManager tm;
    private Logger logger;
    private PageIndex pageIndex;
    private Page pageOne;

    public DataManagerImpl(PageCache pageCache, TransactionManager tm, Logger logger) {
        super(0);
        this.pageCache = pageCache;
        this.tm = tm;
        this.logger = logger;
        this.pageIndex = new PageIndex();
    }

    public Page getPageOne() {
        return this.pageOne;
    }

    @Override
    public DataItem read(long uid) throws Exception {
        DataItemImpl dataItem = (DataItemImpl) super.get(uid);
        if(!dataItem.isValid()) {
            dataItem.release();
            return null;
        }
        return dataItem;
    }

    @Override
    public long insert(long tid, byte[] data) throws Exception {
        byte[] raw = DataItem.wrapDataItemRaw(data);
        //数据长度超过了数据页的最大长度
        if(raw.length > PageCommon.MAX_FREE_SPACE) {
            throw new DatabaseException(ErrorCode.DATA_TOO_LARGE);
        }
        PageInfo pageInfo = null;
        //重试5次
        for(int i = 0; i < 5; i ++) {
            pageInfo = pageIndex.select(raw.length);
            if(pageInfo != null)
                break;
            else {
                //当前内存中的页都满了，重新生成一页
                int newPageNo = pageCache.newPage(PageCommon.initRaw());
                pageIndex.add(newPageNo, PageCommon.MAX_FREE_SPACE);
            }
        }
        //重试5次都不成功，说明数据库繁忙
        if(pageInfo == null)
            throw new DatabaseException(ErrorCode.DATABASE_BUSY);
        //插入数据
        Page page = null;
        int freeSpace = 0;
        try {
            page = pageCache.getPage(pageInfo.getPageNo());
            //操作之前先写日志
            byte[] log = DataRecover.insertLog(tid, page, raw);
            logger.log(log);
            //写入数据
            short offset = PageCommon.insert(page, raw);
            //持久化
            page.release();
            return Types.addressToUid(pageInfo.getPageNo(), offset);
        } finally {
            //取出的page重新放入pageIndex
            if(page != null) {
                pageIndex.add(pageInfo.getPageNo(), PageCommon.getFreeSpace(page));
            } else {
                pageIndex.add(pageInfo.getPageNo(), freeSpace);
            }
        }
    }

    @Override
    public void close() {
        super.close();
        logger.close();
        //正常关闭，首页的校验字符复制
        PageFirst.setRunControlClose(pageOne);
        //持久化
        pageOne.release();
        pageCache.close();
    }

    //tid生成update日志
    public void logDataItem(long tid, DataItem dataItem) {
        byte[] log = DataRecover.updateLog(tid, dataItem);
        logger.log(log);
    }

    public void releaseDataItem(DataItem dataItem) {
        super.release(dataItem.getUid());
    }

    @Override
    protected DataItem getToCache(long key) throws Exception {
        short offset = (short)(key & (1L << 16) - 1);
        key >>>= 32;
        int pageNo = (int)(key & (1L << 32) - 1);
        Page page = pageCache.getPage(pageNo);
        return DataItem.parseDataItem(page,offset, this);
    }

    @Override
    protected void releaseForCache(DataItem obj) {
        obj.page().release();
    }

    //初始化时PageOne
    void initPageOne() {
        int pageNo = pageCache.newPage(PageFirst.initRaw());
        if(pageNo != 1)
            throw new DatabaseException("出错啦, 第一页创建出错");
        try {
            pageOne = pageCache.getPage(pageNo);
        } catch (Exception e) {
            FaultHandler.forcedStop(e);
        }
        //刷入文件
        pageCache.flushPage(pageOne);
    }

    //打开已有文件的时候读入pageOne，验证正确性
    boolean loadCheckPageOne() {
        try {
            pageOne = pageCache.getPage(1);
        } catch (Exception e) {
            FaultHandler.forcedStop(e);
        }
        return PageFirst.checkRunControl(pageOne);
    }

    //初始化pageIndex, 第一页不存储普通的数据，所以从第二页开始存储
    void fillPageIndex() {
        int pageNumber = pageCache.getPageNo();
        for(int i = 2; i <= pageNumber; i ++) {
            Page page = null;
            try {
                page = pageCache.getPage(i);
            } catch (Exception e) {
                FaultHandler.forcedStop(e);
            }
            pageIndex.add(page.getPageNumber(),PageCommon.getFreeSpace(page));
            page.release();
        }
    }
}
